package com.caiden_micheal.menu.service.impl;

import com.alibaba.fastjson2.JSON;
import com.caiden_micheal.base.constant.LoggerHead;
import com.caiden_micheal.base.domain.AjxResult;
import com.caiden_micheal.base.dto.PageDTO;
import com.caiden_micheal.base.utils.BaseContext;
import com.caiden_micheal.base.utils.RedisUtil;
import com.caiden_micheal.base.vo.PageVo;
import com.caiden_micheal.menu.domain.Icon;
import com.caiden_micheal.menu.domain.Menu;
import com.caiden_micheal.menu.dto.IconAddDTO;
import com.caiden_micheal.menu.dto.IconPageQueryDTO;
import com.caiden_micheal.menu.mapper.MenuMapper;
import com.caiden_micheal.menu.service.MenuService;
import com.caiden_micheal.menu.vo.UpdateIndexVO;
import com.caiden_micheal.permission.domain.Permission;
import com.caiden_micheal.permission.mapper.PermissionMapper;
import com.caiden_micheal.permission.service.PermissionService;
import com.caiden_micheal.user.service.UserService;
import com.caiden_micheal.user.vo.AddUserPermissionVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

@Service
public class MenuServiceImpl implements MenuService {
    private static final Logger logger = LoggerFactory.getLogger(MenuServiceImpl.class);
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private UserService userService;

    @Transactional
    @Override
    public AjxResult updateIndex(UpdateIndexVO data) {
        String key = "caiden_micheal:menu:list";
        String allKey = "caiden_micheal:menu:allStatusList";
        if (data == null || data.getId() == null || data.getIndex() == null) {
            return AjxResult.paramError("参数传递有误");
        }
        Menu menu = menuMapper.findById(data.getId());
        if (menu == null) {
            return AjxResult.paramError("菜单不存在");
        }
        try {
            menu = new Menu();
            menu.setId(data.getId());
            menu.setSortIndex(data.getIndex());
            menuMapper.update(menu);
            RedisUtil.deleteKey(key);
            RedisUtil.deleteKey(allKey);
        } catch (Exception e) {
            logger.error(e.toString());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjxResult.unknownError("数据库操作异常");
        }
        return AjxResult.success("调整成功");
    }

    @Override
    public PageDTO iconPageQuery(IconPageQueryDTO iconPageQueryDTO) {
        if (iconPageQueryDTO == null || iconPageQueryDTO.getPage() == null) {
            return null;
        }
        PageVo page = iconPageQueryDTO.getPage();
        page.setCurrentIndex((long) (page.getCurrentPage() - 1) * page.getPageSize());
        List<Icon> iconList = menuMapper.findIconPageQueryByCondition(iconPageQueryDTO);
        page.setCount(menuMapper.findIconPageQueryByConditionCount(iconPageQueryDTO));
        return new PageDTO(iconList, page);
    }

    @Transactional
    @Override
    public AjxResult addMenu(Menu menu) {
        if (menu == null || menu.getParentMenuId() == null || menu.getAuthorityName() == null ||
                menu.getDisplayName() == null || menu.getExternalLink() == null || menu.getIcon() == null ||
                menu.getRelativePath() == null || menu.getSortIndex() == null || menu.getStatus() == null ||
                menu.getUniqueName() == null || menu.getUrlPath() == null) {
            return AjxResult.paramError("参数传入有误");
        }
        try {
            String allStatusKey = "caiden_micheal:menu:allStatusList";
            Permission permission = permissionMapper.findByName(menu.getAuthorityName());
            if (permission != null) {
                return AjxResult.paramError("权限名已存在，请更换后重试");
            }
            permission = new Permission();
            permission.setAuthorityName(menu.getAuthorityName());
            permission.setAuthorityAlias(menu.getDisplayName() + ":menuView");
            permissionMapper.addPermission(permission);
            menu.setAuthorityId(permission.getId());
            menuMapper.addMenu(menu);
            permissionMapper.updateMenuId(permission.getId(), menu.getId());
            ArrayList<Long> userIdList = new ArrayList<>();
            userIdList.add(Long.valueOf(BaseContext.getInfo().get("userId").toString()));
            userService.addUserPermission(new AddUserPermissionVO(userIdList, menu.getAuthorityId()));
            List<String> authorityNameList = userService.findUserAuthorityNameListByUserId(Long.valueOf(BaseContext.getInfo().get("userId").toString()), true);
            List<Menu> menuInfo = this.findEnableMenu(authorityNameList, true);
            List<Menu> allStatusMenuList = recursiveQuerySubmenu(0L, null);
            RedisUtil.setKey(allStatusKey, JSON.toJSONString(allStatusMenuList));
            HashMap<String, List<Menu>> resultData = new HashMap<>();
            resultData.put("openStatusMenuList", menuInfo);
            resultData.put("allStatusMenuList", allStatusMenuList);
            return AjxResult.success(resultData);
        } catch (Exception e) {
            logger.error(e.toString());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjxResult.unknownError("数据库操作异常");
        }
    }

    @Override
    public List<Permission> findAllParentMenuPermissionByPermission(Permission permission) {
        List<Long> permissionIdList = new ArrayList<>();
        Menu menu = menuMapper.findByPermissionId(permission.getId());
        if (menu == null || menu.getParentMenuId() == 0) {
            return null;
        }
        Long parentId = menu.getParentMenuId();
        while (true) {
            Menu tempMenu = menuMapper.findById(parentId);
            if (tempMenu != null) {
                permissionIdList.add(tempMenu.getAuthorityId());
            }
            if (tempMenu == null || tempMenu.getParentMenuId() == 0) {
                break;
            }
            parentId = tempMenu.getParentMenuId();
        }
        return permissionMapper.findBatchByIds(permissionIdList);
    }

    @Override
    public List<Permission> findAllChildMenuPermissionByPermission(Permission permission) {
        Menu menu = menuMapper.findByPermissionId(permission.getId());
        ArrayList<Long> permissionIdList = new ArrayList<>();
        recursiveQuerySubmenuPermissionExtract(menu.getId(), permissionIdList);
        if (permissionIdList.size() != 0) {
            return permissionMapper.findBatchByIds(permissionIdList);
        }
        return null;
    }

    @Override
    public AjxResult findAllMenu() {
        String key = "caiden_micheal:menu:allStatusList";
        List<Menu> menuList;
        try {
            if (RedisUtil.getValue(key) == null) {
                menuList = recursiveQuerySubmenu(0L, null);
                RedisUtil.setKey(key, JSON.toJSONString(menuList));
            } else {
                menuList = JSON.parseArray(RedisUtil.getValue(key), Menu.class);
            }
        } catch (Exception e) {
            logger.error(e.toString());
            return AjxResult.unknownError("数据库操作异常");
        }
        return AjxResult.success(menuList);
    }

    @Transactional
    @Override
    public AjxResult editMenu(Menu menu) {
        if (menu == null || menu.getId() == null || menu.getParentMenuId() == null || menu.getDisplayName() == null ||
                menu.getUrlPath() == null || menu.getRelativePath() == null || menu.getIcon() == null || menu.getSortIndex() == null ||
                menu.getExternalLink() == null || menu.getStatus() == null) {
            logger.warn(LoggerHead.warnHead() + "menu: " + menu);
            return AjxResult.paramError("参数传入有误");
        }
        try {
            menu.setUniqueName(null);
            menu.setAuthorityName(null);
            menu.setAuthorityId(null);
            menuMapper.update(menu);
            String allStatusKey = "caiden_micheal:menu:allStatusList";
            List<String> authorityNameList = userService.findUserAuthorityNameListByUserId(Long.valueOf(BaseContext.getInfo().get("userId").toString()), true);
            List<Menu> menuInfo = this.findEnableMenu(authorityNameList, true);
            List<Menu> allStatusMenuList = recursiveQuerySubmenu(0L, null);
            RedisUtil.setKey(allStatusKey, JSON.toJSONString(allStatusMenuList));
            HashMap<String, List<Menu>> resultData = new HashMap<>();
            resultData.put("openStatusMenuList", menuInfo);
            resultData.put("allStatusMenuList", allStatusMenuList);
            return AjxResult.success(resultData);
        } catch (Exception e) {
            logger.error(e.toString());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjxResult.unknownError("数据库操作异常");
        }
    }

    @Transactional
    @Override
    public AjxResult removeMenu(Long id) {
        if (id == null) {
            return AjxResult.paramError("参数传入有误");
        }
        try {
            Menu menu = menuMapper.findById(id);
            if (menu == null) {
                return AjxResult.paramError("菜单不存在");
            }
            menuMapper.removeById(id);
            List<Long> permissionIds = permissionMapper.findIdByMenuId(id);
            if (permissionIds != null) {
                for (Long permissionId : permissionIds) {
                    permissionService.removePermission(permissionId);
                }
                String keyUser = "caiden_micheal:user:*";
                String keyMenu = "caiden_micheal:menu:*";
                RedisUtil.deleteFold(keyUser);
                RedisUtil.deleteFold(keyMenu);
            }
            return AjxResult.success("菜单已删除，因涉及到权限的刷新问题，正在强制所有相关用户重新登录赋权");
        } catch (Exception e) {
            logger.error(e.toString());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjxResult.unknownError("数据库操作异常");
        }
    }

    @Transactional
    @Override
    public AjxResult addIcon(IconAddDTO iconAddDTO) {
        if (iconAddDTO == null || iconAddDTO.getName() == null) {
            return AjxResult.paramError("参数传递有误");
        }
        try {
            if (iconAddDTO.getId() == null) {
                Icon tempIcon = menuMapper.findIconByName(iconAddDTO.getName());
                if (tempIcon != null) {
                    return AjxResult.paramError("图标已存在");
                }
                Icon icon = new Icon();
                icon.setName(iconAddDTO.getName());
                menuMapper.addIcon(icon);
                icon.setIndex(icon.getId() - 1);
                menuMapper.editIcon(icon);
                return AjxResult.success("添加成功");
            } else {
                Icon icon = new Icon();
                BeanUtils.copyProperties(iconAddDTO, icon);
                menuMapper.editIcon(icon);
                return AjxResult.success("修改成功");
            }
        } catch (Exception e) {
            logger.error(e.toString());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjxResult.unknownError("数据库操作异常");
        }
    }

    @Override
    public List<Menu> findEnableMenu(List<String> authorityNameList, Boolean forceRefresh) {
        String key = "caiden_micheal:menu:list";
        List<Menu> menuList;
        if (forceRefresh || RedisUtil.getValue(key) == null) {
            menuList = recursiveQuerySubmenu(0L, 0);
            RedisUtil.setKey(key, JSON.toJSONString(menuList));
        } else {
            menuList = JSON.parseArray(RedisUtil.getValue(key), Menu.class);
        }
        CopyOnWriteArrayList<Menu> menus = new CopyOnWriteArrayList<>(menuList);
        CopyOnWriteArrayList<Menu> endCopyOnWriteArrayList = recursiveRemoveMenu(menus, authorityNameList);
        if (endCopyOnWriteArrayList == null) {
            return null;
        }
        menuList = endCopyOnWriteArrayList.subList(0, endCopyOnWriteArrayList.size());
        return menuList;
    }

    private CopyOnWriteArrayList<Menu> recursiveRemoveMenu(CopyOnWriteArrayList<Menu> menus, List<String> authorityNameList) {
        if (menus == null) {
            return null;
        } else {
            for (Menu menu : menus) {
                if (!authorityNameList.contains(menu.getAuthorityName())) {
                    menus.remove(menu);
                } else {
                    if (menu.getChild() == null) {
                        continue;
                    }
                    CopyOnWriteArrayList<Menu> tempCopyOnWriteArrayList = recursiveRemoveMenu(new CopyOnWriteArrayList<>(menu.getChild()), authorityNameList);
                    if (tempCopyOnWriteArrayList == null || tempCopyOnWriteArrayList.size() == 0) {
                        menu.setChild(null);
                    } else {
                        menu.setChild(tempCopyOnWriteArrayList.subList(0, tempCopyOnWriteArrayList.size()));
                    }
                }
            }
        }
        if (menus.size() == 0) {
            return null;
        }
        return menus;
    }

    private List<Menu> recursiveQuerySubmenuPermissionExtract(Long parentMenuId, List<Long> permissionIdList) {
        List<Menu> menuList = menuMapper.findByParentId(parentMenuId, null);
        if (menuList == null || menuList.size() == 0) {
            return null;
        }
        for (Menu menu : menuList) {
            permissionIdList.add(menu.getAuthorityId());
            menu.setChild(recursiveQuerySubmenuPermissionExtract(menu.getId(), permissionIdList));
        }
        return menuList;
    }

    private List<Menu> recursiveQuerySubmenu(Long parentMenuId, Integer status) {
        List<Menu> menuList = menuMapper.findByParentId(parentMenuId, status);
        if (menuList == null || menuList.size() == 0) {
            return null;
        }
        for (Menu menu : menuList) {
            menu.setChild(recursiveQuerySubmenu(menu.getId(), status));
        }
        return menuList;
    }
}
